package hibernate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import utils.ReflectionUtils;

public class CommonDAO extends HibernateDaoSupport implements IDao {

//	private static final Log logger = LogFactory.getLog(CommonDAO.class);


	protected JdbcTemplate jdbcTemplate;

	@SuppressWarnings("unchecked")
	public Object findFirst(String hql, Object... args) {
		List list = findAll(hql,args);
		return (list != null && list.size() > 0 ? list.get(0) : null);
	}

	public Integer findInt(String hql, Object... args) {
		Object o = findFirst(hql, args);
		if(o==null) return 0;
		else return Integer.parseInt(String.valueOf(o));
	}

	public Long findLong(String hql, Object... values) {
		return (Long) findFirst(hql, values);
	}

	public String findString(String hql, Object... values) {
		return (String) findFirst(hql, values);
	}

	public <E> E getById(Class<E> clasz, Serializable id, String... includes) {
		return findFirst(clasz, null, "e.id=?", new Object[] { id }, includes);
	}

	public <E> E getByProperty(Class<E> clasz, String name, Object value,
			String... includes) {
		return findFirst(clasz, null, name + "=?", new Object[] { value },
				includes);
	}
	
	public List findAll(String hql, Object... args) {
		List list = getHibernateTemplate().find(hql, args);
		return list;
	}
	public List findAll(Class clasz, String order, String cond,
			Object[] args, String... includes){
//		 from User u left join fetch u.roleList order by u.id
		String hql = "from " + clasz.getName() + " e ";
		if (includes != null && includes.length > 0) {
			for (String include : includes) {
				hql += " left join fetch e." + include + "";
			}
		}
		if (StringUtils.isNotEmpty(cond)) {
			hql += " where (" + insertAlias(cond, clasz) + ")";
		}
		if (StringUtils.isNotEmpty(order))
			hql += " order by e." + order;

		List list = getHibernateTemplate().find(hql, args);
		
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public <E> E findFirst(Class<E> clasz, String order, String cond,
			Object[] args, String... includes) {
		List<E> list = findAll(clasz,order,cond,args,includes);
		return list != null && list.size() > 0 ? list.get(0) : null;
	}

	private String insertAlias(String src, Class clasz) {
		src = " " + src;
		Field[] fields = clasz.getDeclaredFields();
		for (Field field : fields) {
			String name = field.getName();
			src = src.replaceAll(" " + name, " e." + name);
		}
		
		src = src.substring(1);
		return src;
	}

	@SuppressWarnings("unchecked")
	public <E> E findFirst(Class<E> clasz, String orderBy, Boolean isAsc,
			String sqlRestriction, String... includes) {
		List<E> list = getHibernateTemplate().findByCriteria(
				DaoHelper.forClass(clasz, orderBy, isAsc, sqlRestriction,
						includes));

		return list != null && list.size() > 0 ? list.get(0) : null;
	}

	public void saveOrUpdate(Object entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	public void save(Object entity) {
		getHibernateTemplate().save(entity);
	}

	public void update(Object entity) {
		getHibernateTemplate().update(entity);
	}

	public void delete(Object entity) {
		getHibernateTemplate().delete(entity);
	}

	public List findByCriteria(DetachedCriteria dc) {
		return getHibernateTemplate().findByCriteria(dc);
	}
	@SuppressWarnings("unchecked")
	public Page findPage(final DetachedCriteria dc, final int start,
			final int limit,final String... includes) {
		return (Page) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria c = dc.getExecutableCriteria(session);
				CriteriaImpl impl = (CriteriaImpl) c;
				Projection projection = impl.getProjection();
				ResultTransformer transformer = impl.getResultTransformer();

				List<CriteriaImpl.OrderEntry> orderEntries = null;
				orderEntries = (List) ReflectionUtils.getFieldValue(impl, "orderEntries");
				ReflectionUtils.setFieldValue(impl, "orderEntries", new ArrayList());
				// 执行Count查询
				c.setResultTransformer(CriteriaImpl.DISTINCT_ROOT_ENTITY);
				int total = (Integer) c.setProjection(Projections.countDistinct("id"))
						.uniqueResult();

				// 将之前的Projection和OrderBy条件重新设回去
				c.setProjection(projection);
				c.setResultTransformer(transformer);
				ReflectionUtils.setFieldValue(impl, "orderEntries", orderEntries);
				boolean isMap = false;
				
				
				c.setFirstResult(start);
				c.setMaxResults(limit);

				List list = c.list();
				if(list == null) list = new ArrayList();
				
				List tempList = new ArrayList();
				for(int i = 0;i<list.size();i++){
					if(isMap || list.get(i) instanceof Map){
						isMap = true;
						Map itemMap = (Map)list.get(i);
						tempList.add(itemMap.get("this"));
					}
					else{
						tempList.add(list.get(i));
					}
				}
				list = tempList;
				
				
				List returnList = new ArrayList();
				for(Object item : list){
					item = getById(item.getClass(), (Serializable)ReflectionUtils.getFieldValue(item, "id"), includes);
					returnList.add(item);
				}
				
				
				return new Page(start, limit, total, returnList);
			}
		});
	}
	@SuppressWarnings("unchecked")
	public Page findPage(final DetachedCriteria dc, final int start,
			final int limit) {
		return (Page) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria c = dc.getExecutableCriteria(session);
				CriteriaImpl impl = (CriteriaImpl) c;
				Projection projection = impl.getProjection();
				ResultTransformer transformer = impl.getResultTransformer();

				List<CriteriaImpl.OrderEntry> orderEntries = null;
				orderEntries = (List) ReflectionUtils.getFieldValue(impl, "orderEntries");
				ReflectionUtils.setFieldValue(impl, "orderEntries", new ArrayList());
				// 执行Count查询
				c.setResultTransformer(CriteriaImpl.DISTINCT_ROOT_ENTITY);
				int total = (Integer) c.setProjection(Projections.countDistinct("id"))
						.uniqueResult();

				// 将之前的Projection和OrderBy条件重新设回去
				c.setProjection(projection);
				c.setResultTransformer(transformer);
				ReflectionUtils.setFieldValue(impl, "orderEntries", orderEntries);
				boolean isMap = false;
				
				
				c.setFirstResult(start);
				c.setMaxResults(limit);

				List list = c.list();
				if(list == null) list = new ArrayList();
				
				List tempList = new ArrayList();
				for(int i = 0;i<list.size();i++){
					if(isMap || list.get(i) instanceof Map){
						isMap = true;
						Map itemMap = (Map)list.get(i);
						tempList.add(itemMap.get("this"));
					}
					else{
						tempList.add(list.get(i));
					}
				}
				list = tempList;
				
//				if(list.size()<limit){
//					Page p = findPage(dc,start,limit*2);
//				}
				
				
				return new Page(start, limit, total, list);
			}
		});
	}

	public Page findPageBySQL(final String sql, final Object[] args,
			final int start, final int limit) {
		List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();

		String countSQl = "select count(*) " + removeSelect(removeOrders(sql));
		Long count = jdbcTemplate.queryForLong(countSQl, args);

		if (count < 1)
			return new Page(start, limit, Integer.parseInt(String
					.valueOf(count)), new ArrayList());

		data = jdbcTemplate.query(new PreparedStatementCreator() {
			public PreparedStatement createPreparedStatement(Connection arg0)
					throws SQLException {
				PreparedStatement pstmt = arg0.prepareStatement(sql);
				if (args != null) {
					for (int i = 0; i < args.length; ++i)
						pstmt.setObject(i + 1, args[i]);
				}

				if (limit > 0)
					pstmt.setMaxRows(limit + start);

				return pstmt;
			}

		}, new DefaultRowMapper(start, limit));

		return new Page(start, limit, Integer.parseInt(String.valueOf(count)),
				data);
	}

	public Page findPageByHQL(String hql, Object[] args, int start, int limit) {

		String countQueryString = "select count (*) "
				+ removeSelect(removeOrders(hql));
		
		int count = findInt(countQueryString,args);

		if (count < 1)
			new Page(start, limit, count, new ArrayList());

		Query query = createQuery(hql, args);
		List list = query.setFirstResult(start).setMaxResults(limit).list();

		return new Page(start, limit, count, list);
	}

	private Query createQuery(final String queryString, final Object... values) {

		Query query = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}

	@SuppressWarnings("unused")
	private Query createQuery(final String queryString,
			final Map<String, ?> values) {
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			query.setProperties(values);
		}
		return query;
	}

	private static String removeSelect(String hql) {
		int beginPos = hql.toLowerCase().indexOf("from");
		return hql.substring(beginPos);
	}

	private static String removeOrders(String hql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}



	public int executeUpdate(String executeHQL, Object... values) {
		Query query = createQuery(executeHQL, values);
		return query.executeUpdate();
	}
	public int delete(Class clazz, Object id) {
		return executeUpdate("delete " + clazz.getName() + " where id=?", id);
	}
	public int delete(Class clazz, Object[] ids) {
		List<String> list = new ArrayList<String>();
		for (Object i : ids) {
			list.add(String.valueOf(i));
		}
		return executeUpdate("delete " + clazz.getName() + " where id in("
				+ utils.StringHelper.join(list.toArray(new String[0]), ",")
				+ ")");
	}
	
	public void down(Class mappingClass,Integer id,String groupBy){
		orderNumUpOrDown(mappingClass,id,groupBy,true);
	}
	
	public void up(Class mappingClass,Integer id,String groupBy){
		orderNumUpOrDown(mappingClass,id,groupBy,false);
	}
	
	@SuppressWarnings("unchecked")
	private void orderNumUpOrDown(Class mappingClass, Integer id,
			String groupBy, boolean isDown) {
		String orderBy = "position";
		Object curObject = getById(mappingClass, id);
		Integer orderNum = (Integer) ReflectionUtils.getFieldValue(curObject,
				"position");
		if (orderNum == null)
			orderNum = 0;
		DetachedCriteria detachedCriteria = DetachedCriteria
				.forClass(mappingClass);
		Object groupValue = null;

		if (!StringUtils.isBlank(groupBy)) {
			String[] props = groupBy.split("\\.");
			if (props.length == 2) {
				curObject = getById(mappingClass, id, props[0]);
				Object groupObject = ReflectionUtils.getFieldValue(curObject,
						props[0]);
				if (groupObject != null) {
					groupValue = ReflectionUtils.getFieldValue(groupObject,
							props[1]);
				}
			} else {
				groupValue = ReflectionUtils
						.getFieldValue(curObject, groupBy);
			}
			if (groupValue != null) {
				detachedCriteria.add(Restrictions.eq(groupBy, groupValue));
			}
		}
		if (isDown) {
			detachedCriteria.add(Restrictions.ge(orderBy, orderNum));
			detachedCriteria.addOrder(Order.asc(orderBy));
		} else {
			detachedCriteria.add(Restrictions.le(orderBy, orderNum));
			detachedCriteria.addOrder(Order.desc(orderBy));
		}
		
		List objlist = findByCriteria(detachedCriteria);
		if (objlist.size() > 1) {
			Object v0 = ReflectionUtils.getFieldValue(objlist.get(0), orderBy);
			Object v1 = ReflectionUtils.getFieldValue(objlist.get(1), orderBy);

			String hql = "update " + mappingClass.getName() + " set " + orderBy
					+ "=" + v1 + " where id=?";
			executeUpdate(hql);
			hql = "update " + mappingClass.getName() + " set " + orderBy + "="
					+ v0 + " where id=?";
			executeUpdate(hql, objlist.get(0));
		}
	}
	public void saveOrUpdateAll(List list) {
		getHibernateTemplate().saveOrUpdateAll(list);
	}
	public List findByCriteria(DetachedCriteria dc, int start, int limit) {
		return findPage(dc, start, limit).getItems();
	}

	
	
	public Object getMax(Class entityClass,String propertyName,String scope,Object value){
		String selectHql = "select max(a."+propertyName+") from "+entityClass.getName() + " a " ;
		if(value!=null && value instanceof Long && ((Long)value).longValue()==0L){value=null;}
		if(!StringUtils.isEmpty(scope)){
			if(value!=null){
				selectHql +=" where "+scope+" = ?";
				Object result=	findFirst(selectHql, new Object[]{value});
				return result==null ? 0 :result;
			}
				
			else{
				selectHql +=" where "+scope+" is NULL";
				Object result=	findFirst(selectHql);
				return result==null ? 0 :result;
			}
		}
		Object result = findFirst(selectHql, new Object[]{value});
		
		return result==null ? 0 :result;
	}

	public Object getMin(Class entityClass,String propertyName,String scope,Object value){
		String selectHql = "select min(a."+propertyName+") from "+entityClass.getName() + " a " ;
		
		if(!StringUtils.isEmpty(scope)){
			selectHql +=" where "+scope+" = ?";
		}
		Object result = findFirst(selectHql, new Object[]{value});
		
		return result==null ? 0 :result;
	}

	public boolean isExist(Class<?> entityClass, String property, Object value, String whereClause, Object[] values) {
		
		List<Object> params = new ArrayList<Object>();
		if(value!=null)
			params.add(value);
		String selectHql = "select count("+property+") from "+entityClass.getName() + "  where " + property +"=?";
		if(StringUtils.isNotEmpty(whereClause)){
			selectHql = selectHql + " and (";
			selectHql = selectHql + whereClause;
			selectHql = selectHql +")";
		}
		if(values!=null && params.size()>0){
			for(Object param : values){
				params.add(param);
			}
		}
		
		
		return findLong(selectHql, params.toArray(new Object[0]))>0;
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
}
